/***********************************************************
File name     : xjson.c
Author name   : lancz
Email address : chengzhang.lan@jointlighting.com
Create time   : 2022-09-14 09:28:20
***********************************************************/
#define LOG_TAG                    "xjson.c"
#include "ulog.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "xjson.h"
#include "xfile.h"

static xt_s32 _XjsonArrayGetIntIndex(xt_json object, xt_s32 value)
{
    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            xt_s32 index = 0;
            xt_json cptr = object->child;

            while (cptr) {
                if (cptr->type != cJSON_Number) {
                    return XERROR;
                }

                if (cptr->valueint == value) {
                    return index;
                }

                cptr = cptr->next;
                index++;
            }
        }
    }

    return XERROR;
}

static xt_s32 _XjsonArrayGetStringIndex(xt_json object, xt_rostring value)
{
    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            xt_s32 index = 0;
            xt_json cptr = object->child;

            while (cptr) {
                if (cptr->type != cJSON_String) {
                    return XERROR;
                }

                if (strcmp(cptr->valuestring, value) == 0) {
                    return index;
                }

                cptr = cptr->next;
                index++;
            }
        }
    }

    return XERROR;
}

// if key is "a.b.c.d", then parent_path is "a.b.c", key_name is "d"
xt_s32 XjsonParsePath(xt_rostring key,
                      xt_string parent_path, xt_s32 path_size,
                      xt_string key_name, xt_s32 key_size)
{
    xt_string k = (xt_string)key;

    if (NULL == k || NULL == parent_path) {
        return XERROR;
    }

    memset(parent_path, 0, path_size);

    if (key_name) {
        memset(key_name, 0, key_size);
    }

    xt_string first = strchr((xt_string)k, '.');
    xt_string delim = strrchr((xt_string)k, '.');
    if (NULL == delim) { // Not found "."
        memset(parent_path, 0, path_size);
        snprintf(key_name, key_size, "%s", key);
        return XOK;
    }

    if (first == k ||                              // "." is a first char
        delim == k + strlen((xt_rostring)k) - 1) { // "." is a last char
        return XERROR;
    }

    snprintf((xt_string)parent_path, (xt_s32)(delim - k) + 1, "%s", k);

    if (key_name) {
        snprintf((xt_string)key_name, key_size, "%s", delim + 1);
    }

    return XOK;
}

static xt_string Xstrpbrk(xt_rostring cs, xt_rostring ct)
{
    xt_rostring sc1, sc2;
    for (sc1 = cs; *sc1 != '\0'; ++sc1) {
        for (sc2 = ct; *sc2 != '\0'; ++sc2) {
            if (*sc1 == *sc2) {
                return (xt_string)sc1;
            }
        }
    }
    return NULL;
}

static xt_s32 Xstrspn(xt_rostring s, xt_rostring accept)
{
    xt_rostring p;
    xt_rostring a;
    xt_s32 count = 0;

    for (p = s; *p != '\0'; ++p) {
        for (a = accept; *a != '\0'; ++a) {
            if (*p == *a) {
                ++count;
                break;
            }
        }

        if (*a == '\0') {
            return count;
        }
    }
    return count;
}

static xt_string Xstrtok(xt_string s, xt_rostring delim, xt_string *save_ptr)
{
    xt_string token;

    if (s == NULL) {
        s = *save_ptr;
    }

    if (s == NULL) {
        return NULL;
    }

    /* Scan leading delimiters.  */
    // Xprintf("Xstrspn s:%p\n", s);
    s += Xstrspn(s, delim);

    if (*s == '\0') {
        return NULL;
    }

    /* Find the end of the token.  */
    token = s;
    // Xprintf("Xstrpbrk\n");
    s = (xt_string)Xstrpbrk(token, delim);

    if (s == NULL) {
        /* This token finishes the string.  */
        // Xprintf("Xstrchr\n");
        *save_ptr = (xt_string)strchr(token, '\0');
    } else {
        /* Terminate the token and make *SAVE_PTR point past it.  */
        *s = '\0';
        *save_ptr = s + 1;
    }

    // Xprintf("token retun\n");
    return token;
}

xt_json XjsonSearch(xt_json root, xt_rostring path)
{
    xt_s8 path_copy[XJSON_MAX_PATH_LENGTH];
    xt_s8 *p, *q;
    xt_rostring delim = ".";
    xt_json obj = NULL, container = NULL;
    xt_string save;
    snprintf(path_copy, sizeof(path_copy), "%s", path);
    p = path_copy;
    container = root;

    if (NULL == path) {
        return container;
    }

    q = Xstrtok(p, delim, &save);

    while (q) {
        obj = cJSON_GetObjectItem(container, q);

        if (obj) {
            container = obj;
        } else {
            return NULL;
        }

        q = Xstrtok(NULL, delim, &save);
    }
    return obj;
}

xt_json XjsonSearchParent(xt_json root, xt_rostring path)
{
    xt_s8 parent_path[XJSON_MAX_PATH_LENGTH];
    xt_s32 ret = XjsonParsePath(path, parent_path,
                                sizeof(parent_path), NULL, 0);

    if (XOK != ret) {
        return NULL;
    }

    return XjsonSearch(root, parent_path);
}

xt_json XjsonNewParent(xt_json root, xt_rostring path,
                       xt_string node, xt_s32 bufsize)
{
    xt_string last_delim;
    xt_s8 path_copy[XJSON_MAX_PATH_LENGTH];
    xt_string p, q;
    xt_json obj, parent;
    xt_s32 new_object_start = 0;
    xt_string save;
    snprintf(path_copy, sizeof(path_copy), "%s", path);
    parent = root;
    p = path_copy;
    last_delim = strrchr(path_copy, '.');

    if (last_delim == NULL) {
        snprintf(node, bufsize, "%s", path_copy);
        return parent;
    }

    q = Xstrtok(p, ".", &save);

    while (q) {
        // last_delim is points to the '.' delimiter,
        // but q points to the node name
        if (q == last_delim + 1) { // reach to the last node
            snprintf(node, bufsize, "%s", q);
            return parent;
        }

        if (!new_object_start) {
            obj = cJSON_GetObjectItem(parent, q);

            if (obj == NULL) {
                new_object_start = 1;
                obj = cJSON_CreateObject();

                if (obj == NULL) {
                    return NULL;
                }

                cJSON_AddItemToObject(parent, q, obj);
            } else if (obj->type != cJSON_Object) {
                return NULL;
            }

            parent = obj;
        } else {
            obj = cJSON_CreateObject();

            if (obj == NULL) {
                return NULL;
            }

            cJSON_AddItemToObject(parent, q, obj);
            parent = obj;
        }

        q = Xstrtok(NULL, ".", &save);
    }

    return NULL;
}

xt_s32 XjsonGetInt(xt_json root, xt_rostring path,
                   xt_s32 *value, xt_s32 defvalue)
{
    if (NULL == root || NULL == path || NULL == value) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Number) {
            *value = 0;
            return XERROR;
        } else {
            *value = object->valueint;
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    cJSON_AddNumberToObject(parent, node, defvalue);
    *value = defvalue;
    return XOK;
}

xt_s32 XjsonArrayRemoveInt(xt_json root, xt_rostring path, xt_s32 value)
{
    xt_s32 index = XjsonArrayGetIntIndex(root, path, value);

    if (index >= 0) {
        xt_json array = XjsonSearch(root, path);

        if (array) {
            cJSON_DeleteItemFromArray(array, index);
        }
    }

    return XOK;
}

xt_s32 XjsonSetInt(xt_json root, xt_rostring path, xt_s32 value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Number) {
            return XERROR;
        } else {
            cJSON_SetIntValue(object, value);
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    cJSON_AddNumberToObject(parent, node, value);
    return XOK;
}

xt_s32 XjsonSetBool(xt_json root, xt_rostring path, xt_bool value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_True && object->type != cJSON_False) {
            return XERROR;
        } else {
            object->type = !!value;
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    cJSON_AddBoolToObject(parent, node, value);
    return XOK;
}

xt_s32 XjsonGetString(xt_json root, xt_rostring path,
                      xt_string value, xt_s32 bufsize, xt_rostring defvalue)
{
    if (NULL == root || NULL == path || NULL == value || NULL == defvalue) {
        return XERROR_FUNC_ARGS;
    }

    memset(value, 0, bufsize);
    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_String) {
            return XERROR;
        } else {
            snprintf(value, bufsize, "%s", object->valuestring);
            return XOK;
        }
    }
	LOG_I("OBJECT NULL");

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    cJSON_AddStringToObject(parent, node, defvalue);
    snprintf(value, bufsize, "%s", defvalue);
    return XOK;
}

xt_s32 XjsonSetObject(xt_json root, xt_rostring path, xt_json value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Object) {
            return XERROR;
        } else {
            // xt_u8 parent_path[XJSON_MAX_PATH_LENGTH];
            // xt_u8 node_path[XJSON_MAX_PATH_LENGTH];
            XjsonRemove(root, path);
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));
    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    cJSON_AddItemToObject(parent, node, value);
    return XOK;
}

xt_s32 XjsonSetString(xt_json root, xt_rostring path, xt_rostring value)
{
    if (NULL == root || NULL == path || NULL == value) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_String) {
            return XERROR;
        } else {
            if (object->valuestring) {
                free(object->valuestring);
            }

            object->valuestring = strdup(value);
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    cJSON_AddStringToObject(parent, node, value);
    return XOK;
}

xt_s32 XjsonArrayGetStringIndex(xt_json root, xt_rostring path, xt_rostring value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_json object = XjsonSearch(root, path);
    return _XjsonArrayGetStringIndex(object, value);
}

xt_s32 XjsonArrayAddString(xt_json root, xt_rostring path, xt_rostring value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            cJSON_AddItemToArray(object, cJSON_CreateString(value));
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    xt_json array = cJSON_CreateArray();

    if (NULL == array) {
        return XERROR;
    }

    cJSON_AddItemToArray(array, cJSON_CreateString(value));
    cJSON_AddItemToObject(parent, node, array);
    return XOK;
}

xt_s32 XjsonArrayAppendString(xt_json root, xt_rostring path, xt_rostring value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            if (_XjsonArrayGetStringIndex(object, value) >= 0) {
                return XOK;
            }

            cJSON_AddItemToArray(object, cJSON_CreateString(value));
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    xt_json array = cJSON_CreateArray();

    if (NULL == array) {
        return XERROR;
    }

    cJSON_AddItemToArray(array, cJSON_CreateString(value));
    cJSON_AddItemToObject(parent, node, array);
    return XOK;
}

xt_s32 XjsonArrayGetString(xt_json root, xt_rostring path,
                           xt_string values[], xt_s32 maxsize)
{
    if (NULL == root || NULL == path || NULL == values) {
        return XERROR_FUNC_ARGS;
    }

    // Xmemset((xt_u8 *)values, 0, sizeof(values[0]) * maxsize);
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            xt_s32 index = 0;
            xt_json cptr = object->child;

            while (cptr && index < maxsize) {
                if (cptr->type != cJSON_String) {
                    return XERROR;
                }

                values[index] = cptr->valuestring;
                cptr = cptr->next;
                index++;
            }

            return index;
        }
    }

    return XERROR;
}

xt_s32 XjsonArrayRemoveString(xt_json root, xt_rostring path, xt_rostring value)
{
    xt_s32 index = XjsonArrayGetStringIndex(root, path, value);

    if (index >= 0) {
        xt_json array = XjsonSearch(root, path);

        if (array) {
            cJSON_DeleteItemFromArray(array, index);
        }
    }

    return XOK;
}

xt_s32 XjsonArrayAddInt(xt_json root, xt_rostring path, xt_s32 value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            cJSON_AddItemToArray(object, cJSON_CreateNumber(value));
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    xt_json array = cJSON_CreateArray();

    if (NULL == array) {
        return XERROR;
    }

    cJSON_AddItemToArray(array, cJSON_CreateNumber(value));
    cJSON_AddItemToObject(parent, node, array);
    return XOK;
}

xt_s32 XjsonArrayAddObject(xt_json root, xt_rostring path, xt_json value)
{
    if (NULL == root) {
        return XERROR_FUNC_ARGS;
    }

    if (NULL == path) {
        cJSON_AddItemToArray(root, value);
        return XOK;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            cJSON_AddItemToArray(object, value);
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    xt_json array = cJSON_CreateArray();

    if (NULL == array) {
        return XERROR;
    }

    cJSON_AddItemToArray(array, value);
    cJSON_AddItemToObject(parent, node, array);
    return XOK;
}

xt_s32 XjsonArrayAppendInt(xt_json root, xt_rostring path, xt_s32 value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            if (_XjsonArrayGetIntIndex(object, value) >= 0) {
                return XOK;
            }

            cJSON_AddItemToArray(object, cJSON_CreateNumber(value));
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    xt_json array = cJSON_CreateArray();

    if (NULL == array) {
        return XERROR;
    }

    cJSON_AddItemToArray(array, cJSON_CreateNumber(value));
    cJSON_AddItemToObject(parent, node, array);
    return XOK;
}

xt_s32 XjsonLength(xt_json root, xt_rostring path)
{
    if (NULL == root) {
        return XERROR_FUNC_ARGS;
    }

    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Array && object->type != cJSON_Object) {
            return XERROR;
        } else {
            return cJSON_GetArraySize(object);
        }
    }

    return XERROR;
}

xt_s32 XjsonArrayGetIntIndex(xt_json root, xt_rostring path, xt_s32 value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_json object = XjsonSearch(root, path);
    return _XjsonArrayGetIntIndex(object, value);
}

xt_s32 XjsonArrayGetInt(xt_json root, xt_rostring path,
                        xt_s32 values[], xt_s32 maxsize)
{
    if (NULL == root || NULL == path || NULL == values) {
        return XERROR_FUNC_ARGS;
    }

    memset((xt_u8 *)values, 0, sizeof(values[0]) * maxsize);
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            xt_s32 index = 0;
            xt_json cptr = object->child;

            while (cptr && index < maxsize) {
                if (cptr->type != cJSON_Number) {
                    return XERROR;
                }

                values[index] = cptr->valueint;
                cptr = cptr->next;
                index++;
            }

            return index;
        }
    }

    return XERROR;
}

xt_s32 XjsonArrayGetObject(xt_json root, xt_rostring path,
                           xt_json values[], xt_s32 maxsize)
{
    if (NULL == root || NULL == values) {
        return XERROR_FUNC_ARGS;
    }

    if (path == NULL) {
        xt_s32 index = 0;
        xt_json cptr = root->child;
        while (cptr && index < maxsize) {
            if (cptr->type != cJSON_Object && cptr->type != cJSON_Array) {
                return XERROR;
            }

            values[index] = cptr;
            cptr = cptr->next;
            index++;
        }
        return index;
    }

    memset((xt_u8 *)values, 0, sizeof(values[0]) * maxsize);
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Array) {
            return XERROR;
        } else {
            xt_s32 index = 0;
            xt_json cptr = object->child;

            while (cptr && index < maxsize) {
                if (cptr->type != cJSON_Object && cptr->type != cJSON_Array) {
                    return XERROR;
                }

                values[index] = cptr;
                cptr = cptr->next;
                index++;
            }

            return index;
        }
    }

    return XERROR;
}

xt_s32 XjsonKeysCount(xt_json root, xt_rostring path)
{
    if (NULL == root) {
        return XERROR_FUNC_ARGS;
    }

    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Object) {
            return XERROR;
        } else {
            xt_s32 index = 0;
            xt_json cptr = object->child;

            while (cptr) {
                cptr = cptr->next;
                index++;
            }

            return index;
        }
    }
    return XERROR;
}

xt_s32 XjsonKeys(xt_json root, xt_rostring path,
                 xt_string keys[], xt_s32 maxsize)
{
    if (NULL == root || NULL == keys) {
        return XERROR_FUNC_ARGS;
    }

    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Object) {
            return XERROR;
        } else {
            xt_s32 index = 0;
            xt_json cptr = object->child;

            while (cptr && index < maxsize) {
                keys[index] = cptr->string;
                cptr = cptr->next;
                index++;
            }

            return index;
        }
    }

    return XERROR;
}

xt_s32 XjsonRemove(xt_json root, xt_rostring path)
{
    xt_s8 parent_path[XJSON_MAX_PATH_LENGTH];
    xt_s8 node_path[XJSON_MAX_PATH_LENGTH];

    if (NULL == root || NULL == path) {
        return XERROR;
    }

    xt_s32 ret = XjsonParsePath(path, parent_path, sizeof(parent_path),
                                node_path, sizeof(node_path));

    if (ret != XOK) {
        return XERROR;
    }

    xt_json parent = NULL;
    if (strlen(parent_path) == 0)
        parent = root;
    else
        parent = XjsonSearch(root, parent_path);

    if (NULL == parent) {
        return XOK;
    }

    cJSON_DeleteItemFromObject(parent, node_path);
    return XOK;
}

xt_s32 XjsonDump(xt_rostring msg, xt_json object)
{
    xt_s32 ret = 0;
    xt_string objectStr = XjsonToString(object);

    if (msg)
        ret = printf("%s: %s\n", msg, objectStr);
    else
        ret = printf("%s\n", objectStr);

    if (objectStr) {
        free(objectStr);
    }

    return ret;
}

xt_s32 XjsonDumpPretty(xt_rostring msg, xt_json object)
{
    xt_s32 ret = 0;
    xt_string objectStr = XjsonToStringPretty(object);

    if (msg)
        ret = printf("%s: %s\n", msg, objectStr);
    else
        ret = printf("%s\n", objectStr);

    if (objectStr) {
        free(objectStr);
    }

    return ret;
}

xt_json XjsonQueryObject(xt_json root, xt_rostring path)
{
    if (NULL == root || NULL == path) {
        return NULL;
    }

    xt_json object = XjsonSearch(root, path);
    if (object && object->type == cJSON_Object)
        return object;

    return NULL;
}

xt_s32 XjsonQueryBool(xt_json root, xt_rostring path, xt_bool *value)
{
    if (NULL == root || NULL == path || NULL == value) {
        return XERROR_FUNC_ARGS;
    }

    // xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_True && object->type != cJSON_False) {
            *value = 0;
            return XERROR;
        } else {
            *value = object->type == cJSON_True;
            return XOK;
        }
    }

    return XERROR;
}

xt_s32 XjsonQueryInt(xt_json root, xt_rostring path, xt_s32 *value)
{
    if (NULL == root || NULL == path || NULL == value) {
        return XERROR_FUNC_ARGS;
    }

    // xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Number) {
            *value = 0;
            return XERROR;
        } else {
            *value = object->valueint;
            return XOK;
        }
    }

    return XERROR;
}

xt_string XjsonQueryString(xt_json root, xt_rostring path)
{
    if (NULL == root || NULL == path) {
        return NULL;
    }

    // xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_String) {
			LOG_I("TYPE!!!!!:%d", object->type);
            return NULL;
        } else {
            return object->valuestring;
        }
    }

    return NULL;
}

xt_json XjsonNewInt(xt_rostring path, xt_s32 value)
{
    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return json;

    XjsonSetInt(json, path, value);
    return json;
}

xt_json XjsonNewString(xt_rostring path, xt_rostring value)
{
    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return json;

    XjsonSetString(json, path, value);
    return json;
}

xt_json XjsonNewIntFmt(xt_rostring path_fmt, xt_s32 value, ...)
{
    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return json;

    va_list ap;
    xt_s8 path[XJSON_MAX_PATH_LENGTH];
    va_start(ap, value);
    vsnprintf(path, sizeof(path), path_fmt, ap);
    va_end(ap);

    XjsonSetInt(json, path, value);
    return json;
}

xt_json XjsonNewStringFmt(xt_rostring path_fmt, xt_rostring value, ...)
{
    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return json;

    va_list ap;
    xt_s8 path[XJSON_MAX_PATH_LENGTH];
    va_start(ap, value);
    vsnprintf(path, sizeof(path), path_fmt, ap);
    va_end(ap);

    XjsonSetString(json, path, value);
    return json;
}

xt_json XjsonNewBool(xt_rostring path, xt_bool value)
{
    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return json;

    XjsonSetBool(json, path, value);
    return json;
}

#ifdef __linux__
xt_json XjsonNewBoolFmt(xt_rostring path_fmt, xt_bool value, ...)
{
    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return json;

    va_list ap;
    xt_s8 path[XJSON_MAX_PATH_LENGTH];
    va_start(ap, value);
    vsnprintf(path, sizeof(path), path_fmt, ap);
    va_end(ap);

    XjsonSetBool(json, path, value);
    return json;
}
#endif

xt_s32 XjsonSetDouble(xt_json root, xt_rostring path, double value)
{
    if (NULL == root || NULL == path) {
        return XERROR_FUNC_ARGS;
    }

    xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Number) {
            return XERROR;
        } else {
            cJSON_SetIntValue(object, value);
            return XOK;
        }
    }

    xt_json parent = XjsonNewParent(root, path, node, sizeof(node));

    if (!parent || parent->type != cJSON_Object) {
        return XERROR;
    }

    cJSON_AddNumberToObject(parent, node, value);
    return XOK;
}

xt_json XjsonNewDouble(xt_rostring path, double value)
{
    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return json;

    XjsonSetDouble(json, path, value);
    return json;
}

xt_s32 XjsonQueryDouble(xt_json root, xt_rostring path, double *value)
{
    if (NULL == root || NULL == path || NULL == value) {
        return XERROR_FUNC_ARGS;
    }

    // xt_s8 node[XJSON_MAX_PATH_LENGTH];
    xt_json object = XjsonSearch(root, path);

    if (object) {
        if (object->type != cJSON_Number) {
            *value = 0.0;
            return XERROR;
        } else {
            *value = object->valuedouble;
            return XOK;
        }
    }
    return XERROR;
}

xt_json XjsonNewDoubleFmt(xt_rostring path_fmt, double value, ...)
{
    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return json;

    va_list ap;
    xt_s8 path[XJSON_MAX_PATH_LENGTH];
    va_start(ap, value);
    vsnprintf(path, sizeof(path), path_fmt, ap);
    va_end(ap);

    XjsonSetDouble(json, path, value);
    return json;
}

xt_s32 XjsonSetIntFmt(xt_json root, xt_rostring path_fmt, xt_s32 value, ...)
{
    va_list ap;
    xt_s8 path[XJSON_MAX_PATH_LENGTH];
    va_start(ap, value);
    vsnprintf(path, sizeof(path), path_fmt, ap);
    va_end(ap);

    return XjsonSetInt(root, path, value);
}

#ifdef __linux__
xt_s32 XjsonSetBinary(xt_json root, xt_rostring path, void *value, xt_s32 size)
{
    if (NULL == root || NULL == path || NULL == value)
        return XERROR;

    xt_s32 memsize = size + Xbase64TagLength();
    if (size <= 4)
        memsize *= 5;
    else
        memsize *= 2;
    void *base64 = malloc(memsize);
    if (NULL == base64)
        return XERROR;

    Xbase64TagEncode(value, size, base64, memsize);
    XjsonSetString(root, path, base64);
    free(base64);
    return XOK;
}

xt_json XjsonNewBinary(xt_rostring path, void *value, xt_s32 size)
{
    if (NULL == path || NULL == value)
        return NULL;

    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return NULL;

    XjsonSetBinary(json, path, value, size);
    return json;
}

xt_json XjsonNewBinaryFmt(xt_rostring path_fmt, void *value, xt_s32 size, ...)
{
    xt_json json = XjsonCreateObject();
    if (NULL == json)
        return json;

    va_list ap;
    xt_s8 path[XJSON_MAX_PATH_LENGTH];
    va_start(ap, size);
    vsnprintf(path, sizeof(path), path_fmt, ap);
    va_end(ap);

    XjsonSetBinary(json, path, value, size);
    return json;
}

xt_s32 XjsonQueryBinary(xt_json root, xt_rostring path, void *buf, xt_s32 bufsize)
{
    if (NULL == root || NULL == path || NULL == buf)
        return XERROR;

    xt_string base64 = XjsonQueryString(root, path);
    if (NULL == base64)
        return XERROR;

    return Xbase64TagDecode(base64, buf, bufsize);
}
#endif

xt_json XjsonMerge(xt_json target, xt_json add)
{
    if (NULL == target)
        return NULL;

    if (NULL == add)
        return target;

    xt_s32 key_count = XjsonKeysCount(add, NULL);
    if (key_count <= 0) {
        //        XjsonDelete(add);
        return target;
    }

    xt_string *keys = malloc(key_count * (sizeof(xt_string)));
    if (NULL == keys) {
        //        XjsonDelete(add);
        return target;
    }

    key_count = XjsonKeys(add, NULL, keys, key_count);
    xt_s32 i = 0;
    for (i = 0; i < key_count; i++) {
        xt_string key = keys[i];
        xt_json target_item = XjsonSearch(target, key);

		if (!target_item) {
			continue;
		}

		xt_json add_item = XjsonSearch(add, key);
		if (NULL == add_item)
			continue;

		switch (add_item->type) {
			case cJSON_String:
				XjsonSetString(target, key, add_item->valuestring);
				break;
			case cJSON_Number:
				XjsonSetInt(target, key, add_item->valueint);
				break;
			case cJSON_False:
				XjsonSetBool(target, key, false);
				break;
			case cJSON_True:
				XjsonSetBool(target, key, true);
				break;
			case cJSON_Object:
				XjsonMerge(target_item, add_item);
				break;
		}
    }

    free(keys);
    //    XjsonDelete(add);
    return target;
}

static xt_json xjsonArrayGetItem(xt_json array, xt_s32 index)
{
    if (array == NULL || index < 0)
        return NULL;

    xt_s32 count = XjsonLength(array, NULL);
    if (count <= 0)
        return NULL;

    if (index > count - 1)
        return NULL;

    return cJSON_GetArrayItem(array, index);
}

xt_json XjsonArrayGetItem(xt_json array, xt_s32 index)
{
    return xjsonArrayGetItem(array, index);
}

xt_s32 XjsonArrayGetIntItem(xt_json array, xt_s32 index, xt_s32 *value)
{
    if (NULL == value)
        return XERROR_FUNC_ARGS;

    xt_json item = xjsonArrayGetItem(array, index);
    if (NULL == item)
        return XERROR;

    if (item->type != cJSON_Number)
        return XERROR;

    *value = item->valueint;
    return XOK;
}

xt_string XjsonArrayGetStringItem(xt_json array, xt_s32 index)
{
    xt_json item = xjsonArrayGetItem(array, index);
    if (NULL == item)
        return NULL;

    if (item->type != cJSON_String)
        return NULL;

    return item->valuestring;
}

xt_s32 XjsonArrayGetDoubleItem(xt_json array, xt_s32 index, double *value)
{
    if (NULL == value)
        return XERROR_FUNC_ARGS;

    xt_json item = xjsonArrayGetItem(array, index);
    if (NULL == item)
        return XERROR;

    *value = item->valuedouble;
    return XOK;
}

xt_s32 XjsonSetInt64(xt_json root, xt_rostring path, xt_s64 value)
{
    xt_double dvalue = (xt_double)value;
    return XjsonSetDouble(root, path, dvalue);
}

xt_json XjsonNewInt64(xt_rostring path, xt_s64 value)
{
    xt_double dvalue = (xt_double)value;
    return XjsonNewDouble(path, dvalue);
}

xt_s32 XjsonQueryInt64(xt_json root, xt_rostring path, xt_s64 *value)
{
    double dvalue = (double)*value;
    xt_s32 ret = XjsonQueryDouble(root, path, &dvalue);
    *value = (xt_s64)dvalue;
    return ret;
}

xt_json XjsonLoad(xt_rostring path)
{
    if (NULL == path)
        return NULL;

    xt_s32 size = XfileSize(path);
    if (size <= 0) {
        LOG_D("%s size:%d <= 0", path);
        return NULL;
    }

    xt_string str = malloc(size + 1);
    if (NULL == str) {
        LOG_E("malloc failed");
        return NULL;
    }

    xt_s32 ret = XfileRead(path, str, size);
    if (ret <= 0) {
        LOG_E("read %s failed", path);
        free(str);
        return NULL;
    }

    xt_json json = XjsonFromString(str);
    free(str);
    return json;
}

xt_s32 XjsonSave(xt_rostring path, xt_json json)
{
    if (NULL == path || NULL == json)
        return XERROR_FUNC_ARGS;

    xt_string str = XjsonToString(json);
    if (NULL == str)
        return XERROR;

    xt_s32 ret = XfileWrite(path, str, strlen(str));
    free(str);
    return ret;
}
